package com.cershy.linyuminiserver.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cershy.linyuminiserver.constant.DeviceConstant;
import com.cershy.linyuminiserver.constant.DeviceType;
import com.cershy.linyuminiserver.constant.NotifyType;
import com.cershy.linyuminiserver.constant.SystemConstants;
import com.cershy.linyuminiserver.dto.DeviceDto;
import com.cershy.linyuminiserver.dto.NotifyDto;
import com.cershy.linyuminiserver.entity.Device;
import com.cershy.linyuminiserver.mapper.DeviceMapper;
import com.cershy.linyuminiserver.service.DeviceService;
import com.cershy.linyuminiserver.service.WebSocketService;
import com.cershy.linyuminiserver.utils.ResultUtil;
import com.github.houbb.heaven.constant.SystemConst;
import io.swagger.models.auth.In;
import lombok.RequiredArgsConstructor;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.domain.geo.GeoReference;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {

    private final StringRedisTemplate stringRedisTemplate;

    private final WebSocketService webSocketService;

    @Override
    public void addDeviceLocation(String deviceId, double latitude, double longitude) {

    }

    @Override
    public Object addDevice(DeviceDto deviceAdd) {

        Device device = BeanUtil.toBean(deviceAdd, Device.class);
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        if(getOne(queryWrapper.eq(Device::getDeviceCode,device.getDeviceCode()))!=null){
            return ResultUtil.Fail("设备码已存在！");
        }
        save(device);
        return ResultUtil.Succeed();
    }

    @Override
    public Object  getDeviceList(DeviceType type, Double x, Double y, Double distance, Integer current) {
        if(x==null||y==null){
            Page<Device>page=query()
                    .eq(type!=null,"type",type != null ? type.getCode() : null)
                    .page(new Page<>(1, SystemConstants.DEFAULT_PAGE_SIZE));
            return ResultUtil.Succeed(page.getRecords());
        }

        //计算分页参数
        int from=(current-1)*SystemConstants.DEFAULT_PAGE_SIZE;
        int end=current*SystemConstants.DEFAULT_PAGE_SIZE;

        //查询redis
        String key= DeviceConstant.DEVICE_KEY + ":" +type.getCode();
        System.out.println("查询的是"+key);
        GeoResults<RedisGeoCommands.GeoLocation<String>> search = stringRedisTemplate.opsForGeo().search(
                key,
                GeoReference.fromCoordinate(x, y),
                new Distance(distance),
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                        .includeCoordinates().limit(end)
        );

        //解析出id
        if(search==null){
            return ResultUtil.Succeed(Collections.emptyList());
        }
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = search.getContent();
        //截取from-end的集合
        List<String>codes=new ArrayList<>(content.size());
        Map<String,Distance> distanceMap=new HashMap<>(content.size());
        content.stream().skip(from).forEach(result->{
            //获取设备码
            String deviceCode = result.getContent().getName();
            Distance ds = result.getDistance();
            codes.add(deviceCode);
            distanceMap.put(deviceCode,ds);
        });

        //如果空集返回
        if (codes == null || codes.isEmpty()) {
            return Collections.emptyList(); // 空集合直接返回，避免生成无效SQL
        }

        //根据code查询设备
        // 修改点：为每个code添加单引号包裹
        String codeStr = codes.stream()
                .map(code -> "'" + code + "'")
                .collect(Collectors.joining(","));
        List<Device> devices = query().in("device_code", codes)
                .last("ORDER BY FIELD(device_code," + codeStr + ")").list();
        for(Device d:devices){
            d.setDistance(distanceMap.get(d.getDeviceCode()).getValue());
        }
        return ResultUtil.Succeed(devices);
    }

    @Override
    public void addAllDeviceLocation() {
        List<Device> list = list();

        System.out.println("添加预缓存");

        //根据类型进行分类
        Map<Integer,List<Device>> map=list.stream().collect(Collectors.groupingBy(Device::getType));

        //分批写入redis
        for (Map.Entry<Integer,List<Device>> entry : map.entrySet()) {
            Integer type = entry.getKey();

            String key= DeviceConstant.DEVICE_KEY+":"+type;

            //获取同类型的集合
            List<Device> value=entry.getValue();

            List<RedisGeoCommands.GeoLocation<String>>locations=new ArrayList<>(value.size());

            for (Device device : value) {
                // 一个一个加请求太多
                //stringRedisTemplate.opsForGeo().add(key, new Point(device.getX(),device.getY()), device.getId().toString());

                //同时添加
                locations.add(new RedisGeoCommands.GeoLocation<String>(
                        device.getDeviceCode(),
                        new Point(device.getX(), device.getY())
                ));
            }

            stringRedisTemplate.opsForGeo().add(key, locations);

        }
    }

    @Override
    public void online(String deviceCode) {
        NotifyDto notifyDto = new NotifyDto();
        notifyDto.setTime(new Date());
        notifyDto.setType(NotifyType.Web_Online);
        notifyDto.setContent(deviceCode);
        webSocketService.sendNotifyToGroup(notifyDto);
    }
}
